#include <curses.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <unistd.h>
int currentLen;
typedef struct _g
{
    int x;
    int y;
    int isHead;
    struct _g* next;
    struct _g* prev;
}gamenode;
gamenode head;
int dir;
int predir;
int fx,fy;
void changedir(int d){
    if(abs(dir-d)!=2)
        predir=d;
}
void * thread1_proc(void* _){
    _=_;
    erase();
    refresh();
    while(1){
        int ch=getch();
        switch (ch) {
        case KEY_DOWN:
            changedir(1);
            break;
        case KEY_UP:
            changedir(3);
            break;
        case KEY_RIGHT:
            changedir(2);
            break;
        case KEY_LEFT:
            changedir(0);
            break;
        default:
            break;
        }
    }

}
void generatefood(){
    do{
        int x=rand()%(LINES-2)+1;
        int y=rand()%(COLS-2)+1;
        int i=1;
        gamenode *h=&head;
        while(h->next!=NULL){
            if(h->x==x&&h->y==y){
                i=0;
                break;
            }
            h=h->next;
        }
        if(i==1){
            fx=x;
            fy=y;
            break;
        }
    }while(1);
}
void eat(){
    gamenode* g=(gamenode*)malloc(sizeof(gamenode));
    g->isHead=TRUE;
    g->x=fx;
    g->y=fy;
    gamenode*h=&head;
    g->prev=h;
    g->next=h->next;
    h->next->prev=g;
    h->next->isHead=FALSE;
    h->next=g;
    generatefood();
}
void resetgamenode(gamenode* head){
    gamenode* h=head;
    while(h->next!=NULL){
        gamenode* g=h->next->next;
        free(h->next);
        h->next=g;
    }
}
void initgame(){

    currentLen=3;
    predir=dir=2;
    if(head.next!=NULL)
        resetgamenode(&head);
    gamenode *h=&head;
    for(int i=0;i<currentLen;i++){
        gamenode* g=(gamenode*)malloc(sizeof(gamenode));
        g->isHead=i==0?1:0;
        g->x=LINES/2;
        g->y=COLS/4-i;
        g->next=NULL;
        g->prev=h;
        h->next=g;
        h=g;
    }
    generatefood();

}
void display(){

    clear();
    box(stdscr,'#','#');
    gamenode* h=&head;
    while(h->next!=NULL){
        move(h->next->x,h->next->y);
        if(h->next->isHead){
            addch('@');
        }else{
            addch('$');
        }
        h=h->next;
    }
    move(fx,fy);
    addch('*');
    refresh();

}
void movenext(){

    gamenode* h=&head;

    while(h->next!=NULL){
        h=h->next;
    }
    while(h->prev!=NULL&&h->prev->prev!=NULL){
        h->x=h->prev->x;
        h->y=h->prev->y;
        h=h->prev;
    }
    h=&head;
    switch (dir) {
    case 0:
        h->next->y-=1;
        break;
    case 1:
        h->next->x+=1;
        break;
    case 2:
        h->next->y+=1;
        break;
    case 3:
        h->next->x-=1;
        break;
    default:
        break;
    }

    display();
    dir=predir;
}
int check(){
    int x,y;
    int ret=0;
    gamenode* h=&head;
    switch (dir) {
    case 0:
        x=h->next->x;
        y=h->next->y-1;
        break;
    case 1:
        x=h->next->x+1;
        y=h->next->y;
        break;
    case 2:
        x=h->next->x;
        y=h->next->y+1;
        break;
    case 3:
        x=h->next->x-1;
        y=h->next->y;
        break;
    default:
        break;
    }
    if(x==fx&&y==fy){
        eat();
        return 0;
    }
    if(x<=0||y<=0||x>=LINES-1||y>=COLS-1)return 1;
    while(h->next!=NULL){
        if(h->next->x==x&&h->next->y==y){
            ret=1;
            break;
        }
        h=h->next;
    }
    return ret;
}
int main(){

    srand(time(NULL));
    head.next=NULL;
    head.prev=NULL;
    pthread_t thread1;
    initscr();
    noecho();
    curs_set(0);
    crmode();
    keypad(stdscr,TRUE);
    char s[]="Press Enter to Play!";
    int l=strlen(s);
    move(LINES/2,(COLS-l)/2);
    printw(s);
    while('\n'!=getch());
    initgame();
    pthread_create(&thread1,NULL,thread1_proc,NULL);
    while(check()==0){

        movenext();
        usleep(100000);
    }
    pthread_cancel(thread1);
    clear();
    char t[]="Game Over! Press any key to Quit!";
    l=strlen(t);
    move(LINES/2,(COLS-l)/2);
    printw(t);
    refresh();
    getch();
    endwin();
    exit(EXIT_SUCCESS);
    return 0;
}
